package org.anderground.editor.edits;

import java.util.Observable;
import java.util.Observer;

import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;

import org.anderground.core.model.ModelData;
import org.anderground.editor.model.EditorModel;

/**
 * @author Eldar Abusalimov
 */
public class EditsManager implements Observer {

	private static EditsManager instance = new EditsManager();

	private UndoManager undoManager = new UndoManager();

	private ModelData managedModelData = getModel().getData();

	private EditsManager() {
		getModel().addObserver(this);
	}

	public static EditsManager getInstance() {
		return instance;
	}

	private static EditorModel getModel() {
		return EditorModel.getInstance();
	}

	@Override
	public void update(Observable o, Object arg) {
		if (managedModelData != getModel().getData()) {
			undoManager.discardAllEdits();
			managedModelData = getModel().getData();
		}
	}

	/**
	 * 
	 * Perform the action adding it to the {@link EditsManager}. Note that the
	 * action can be performed only once.
	 * 
	 * @param action
	 *            the action to perform
	 * @return true if the action was performed successfully, false otherwise
	 */
	public boolean performAction(Action action) {
		try {
			if (action.isDone()) {
				return false;
			}
			UndoableEdit edit = action.perform();
			action.setDone();
			boolean b = undoManager.addEdit(edit);
			getModel().setChanged();
			getModel().notifyObservers();
			return b;
		} catch (ActionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * @throws CannotRedoException
	 * @see javax.swing.undo.UndoManager#redo()
	 */
	public void redo() throws CannotRedoException {
		undoManager.redo();
		getModel().notifyObservers();
	}

	/**
	 * @throws CannotUndoException
	 * @see javax.swing.undo.UndoManager#undo()
	 */
	public void undo() throws CannotUndoException {
		undoManager.undo();
		getModel().setChanged();
		getModel().notifyObservers();
	}

	/**
	 * @return
	 * @see javax.swing.undo.UndoManager#canRedo()
	 */
	public boolean canRedo() {
		return undoManager.canRedo();
	}

	/**
	 * @return
	 * @see javax.swing.undo.UndoManager#canUndo()
	 */
	public boolean canUndo() {
		return undoManager.canUndo();
	}

	/**
	 * @return
	 * @see javax.swing.undo.UndoManager#canUndoOrRedo()
	 */
	public boolean canUndoOrRedo() {
		return undoManager.canUndoOrRedo();
	}

	/**
	 * @return
	 * @see javax.swing.undo.CompoundEdit#getPresentationName()
	 */
	public String getPresentationName() {
		return undoManager.getPresentationName();
	}

	/**
	 * @return
	 * @see javax.swing.undo.UndoManager#getRedoPresentationName()
	 */
	public String getRedoPresentationName() {
		return undoManager.getRedoPresentationName();
	}

	/**
	 * @return
	 * @see javax.swing.undo.UndoManager#getUndoPresentationName()
	 */
	public String getUndoPresentationName() {
		return undoManager.getUndoPresentationName();
	}

	/**
	 * @throws CannotRedoException
	 * @throws CannotUndoException
	 * @see javax.swing.undo.UndoManager#undoOrRedo()
	 */
	public void undoOrRedo() throws CannotRedoException, CannotUndoException {
		undoManager.undoOrRedo();
	}

}
